description  'Wiki syntax tags for scripting'
require 'evaluator'

Tag.define :value, requires: 'of', immediate: true, description: 'Print value' do |context, attrs|
  Evaluator.eval(attrs['of'], context.params)
end

Tag.define :def, optional: %w(value args), requires: 'name',
                 immediate: true, description: 'Define variable' do |context, attrs, content|
  name = attrs['name'].downcase
  if attrs['value']
    context.params[name] = Evaluator.eval(attrs['value'], context.params)
  else
    functions = context[:functions] ||= {}
    functions[name] = [attrs['args'].to_s.split(/\s+/), content]
  end
  nil
end

Tag.define :call, optional: '*', requires: 'name', immediate: true, description: 'Call function' do |context, attrs|
  name = attrs['name'].downcase
  functions = context[:functions]
  raise NameError, "Function #{name} not found" if !functions || !functions[name]
  args, content = functions[name]
  args = args.inject({}) do |hash, arg|
    raise ArgumentError, "Argument #{arg} is required" if !attrs[arg]
    hash[arg] = Evaluator.eval(attrs[arg], context.params)
    hash
  end
  result = nested_tags(context.subcontext(params: args), content)
  if attrs['result']
    context.params[attrs['result']] = result
    nil
  else
    result
  end
end

Tag.define :for, optional: 'counter', requires: %w(from to),
                 immediate: true, limit: 50, description: 'For loop' do |context, attrs, content|
  to = attrs['to'].to_i
  from = attrs['from'].to_i
  raise 'Limits exceeded' if to - from > 10
  (from..to).map do |i|
    params = attrs['counter'] ? {attrs['counter'] => i} : {}
    nested_tags(context.subcontext(params: params), content)
  end.join
end

Tag.define :repeat, optional: 'counter', requires: 'times',
           immediate: true, limit: 50, description: 'Repeat loop' do |context, attrs, content|
  n = attrs['times'].to_i
  raise 'Limits exceeded' if n > 10
  (1..n).map do |i|
    params = attrs['counter'] ? {attrs['counter'] => i} : {}
    nested_tags(context.subcontext(params: params), content)
  end.join
end

Tag.define :if, requires: 'test', immediate: true, description: 'If statement' do |context, attrs, content|
  if Evaluator.eval(attrs['test'], context.params)
    nested_tags(context.subcontext, content)
  end
end
